﻿#include "pch.h"
#if _QG_USE_D11
#include "qgp/qgach.h"
#include "qgdrv_d11_rdh.h"
#include "qg_trace.h"

//////////////////////////////////////////////////////////////////////////
// 렌더 타겟 특히, 기본 렌더 타겟

//
D11SwapChain::D11SwapChain()
{
}

//
D11SwapChain::~D11SwapChain()
{
	// 어짜피 Dispose() 강제로 호출하기 때문에 패스
	//Dispose();
}

//
void D11SwapChain::Dispose()
{
	kunk_reset(&_rtv);
	kunk_reset(&_dsv);
	kunk_reset(&_tex);
	kunk_reset(&_sc);
}

//
HRESULT D11SwapChain::Create(IDXGIFactory* factory, ID3D11Device* device, DXGI_SAMPLE_DESC aasd, tpl::Point size, HWND hwnd, bool windowed)
{
	HRESULT hr;

	// 스왑체인
	DXGI_SWAP_CHAIN_DESC scd;
	scd.BufferDesc.Width = size.width;
	scd.BufferDesc.Height = size.height;
	scd.BufferDesc.RefreshRate.Numerator = 0;
	scd.BufferDesc.RefreshRate.Denominator = 1;
	scd.BufferDesc.Format = D11SURFACEFORMAT;
	scd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
	scd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
	scd.SampleDesc = aasd;
	scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	scd.BufferCount = 1;
	scd.OutputWindow = hwnd;
	scd.Windowed = windowed;
	scd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
	scd.Flags = 0;

	hr = factory->CreateSwapChain(device, &scd, &_sc);
	if (FAILED(hr))
		return hr;

	// 레이아웃에서 나머지 만듦
	return Layout(device, aasd, size);
}

//
HRESULT D11SwapChain::Layout(ID3D11Device* device, DXGI_SAMPLE_DESC aasd, tpl::Point size)
{
	HRESULT hr;

	// 먼저 제거
	kunk_reset(&_rtv);
	kunk_reset(&_dsv);
	kunk_reset(&_tex);

	// 뷰포트
	_vp.TopLeftX = 0.0f;
	_vp.TopLeftY = 0.0f;
	_vp.Width = (float)size.width;
	_vp.Height = (float)size.height;
	_vp.MinDepth = 0.0f;
	_vp.MaxDepth = 1.0f;

	// 스왑체인 크기 변경
	if (FAILED(hr = _sc->ResizeBuffers(1, size.width, size.height, D11SURFACEFORMAT, 0)))
		return hr;

	// 렌더 타깃 뷰
	ID3D11Texture2D* backbuffer;
	if (FAILED(hr = _sc->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backbuffer)))
	{
		// 백버퍼 얻기 실패
		return hr;
	}

	hr = device->CreateRenderTargetView(backbuffer, 0, &_rtv);

	backbuffer->Release();

	if (FAILED(hr))
	{
		// 렌더 타겟뷰 만들기 실패
		return hr;
	}

	// 깊이 스텐실 버퍼 및 뷰
	D3D11_TEXTURE2D_DESC dsd;
	dsd.Width = size.width;
	dsd.Height = size.height;
	dsd.MipLevels = 1;
	dsd.ArraySize = 1;
	dsd.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
	dsd.SampleDesc = aasd;
	dsd.Usage = D3D11_USAGE_DEFAULT;
	dsd.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	dsd.CPUAccessFlags = 0;
	dsd.MiscFlags = 0;

	if (FAILED(hr = device->CreateTexture2D(&dsd, 0, &_tex)))
		return hr;

	if (FAILED(hr = device->CreateDepthStencilView(_tex, 0, &_dsv)))
		return hr;

	return S_OK;
}

//
bool D11SwapChain::Flush()
{
	return SUCCEEDED(_sc->Present(0, 0));
}

//
void D11SwapChain::Render(ID3D11DeviceContext* context)
{
	context->OMSetRenderTargets(1, &_rtv, _dsv);
	context->RSSetViewports(1, &_vp);
}


//////////////////////////////////////////////////////////////////////////
// 다이나믹 버퍼

//
D11DynBuffer::D11DynBuffer(D11Rdh* rdh, qgBufferType type, kint sizes[MAXBUFFER])
	: _index(-1)
	, _type(type)
	, _rdh(rdh)
{
	for (kint i = 0; i < MAXBUFFER; i++)
	{
		if (sizes[i] == 0)
			break;

		_size[i] = sizes[i];

		_ptr[i] = (D11Buffer*)rdh->CreateBuffer(type, 1, sizes[i], QGRESF_DYNAMIC, NULL);

		if (!_ptr[i])
			k_traces(KLV_ERROR, "d11.dynbuf", "cannot create buffer.");
	}
}

//
D11DynBuffer::~D11DynBuffer()
{
	for (kint i = 0; i < MAXBUFFER; i++)
		_ptr[i]->Unload();
}

//
kpointer D11DynBuffer::Enter(kint size)
{
	if (_index >= 0)
	{
		k_tracef(KLV_ERROR, "d11.dynbuf", "do not enter locked buffer. (lock index: %d)", _index);
		return NULL;
	}

	//
	kint ind = 0;
	const kint cnt = MAXBUFFER;

	while (ind < cnt && _size[ind] < size)
		ind++;

	//
	if (ind == cnt)
	{
		ind--;

		D11Buffer* ptr = (D11Buffer*)_rdh->CreateBuffer(_type, 1, size, QGRESF_DYNAMIC, NULL);
		if (!ptr)
			k_traces(KLV_ERROR, "d11.dynbuf", "cannot create buffer.");

		_ptr[ind] = ptr;
		_size[ind] = size;
	}

	//
	_index = ind;

	kpointer ret = _ptr[ind]->D11Buffer::Map(QGENTF_WRITE);

	if (!ret)
	{
		k_traces(KLV_ERROR, "d11.dynbuf", "cannot enter mapping buffer.");
		return NULL;
	}

	return ret;
}

//
D11Buffer* D11DynBuffer::Leave()
{
	if (_index < 0)
	{
		k_tracef(KLV_ERROR, "d11.dynbuf", "do not leave unlocked buffer. (lock index: %d)", _index);
		return NULL;
	}

	D11Buffer* ptr = _ptr[_index];
	ptr->D11Buffer::Unmap();

	_index = -1;

	return ptr;
}

#endif	// _QG_USE_D11
